package com.yxb.cms.architect.utils;

import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import java.io.UnsupportedEncodingException;

/**
 * 十六进制字符串加密解密实现
 * @author yangbingbing
 */
@Log4j2
public class HexUtil {


	private HexUtil(){}

	/**
	 * 加密
	 * @param plain 明文
	 * @param charset 明文字符编码
	 * @param hexKey 十六进制字符串形式密钥
	 * @return 十六进制字符串形式密文
	 */
	public static String encrypt(String plain, String charset, String hexKey) throws Exception {
		if (plain == null || charset == null || hexKey == null) {
				return null;
		}
		try {
			return Hex.encodeHexString((encrypt(plain.getBytes(charset), Hex.decodeHex(hexKey))), false);
		} catch (Exception e) {
			log.error("加密异常{}",e.getMessage(),e);
			throw e;
		}
	}
	
	/**
	 * 解密
	 * @param cipherHex 十六进制字符串形式密文
	 * @param charset 明文字符编码
	 * @param hexKey 十六进制字符串形式密钥
	 * @return 明文
	 */
	public static String decrypt(String cipherHex, String charset, String hexKey) throws Exception {
		if (cipherHex == null || charset == null || hexKey == null) {
			return null;
		}
		try {
			return new String(decrypt(Hex.decodeHex(cipherHex), Hex.decodeHex(hexKey)), charset);
		} catch (Exception e) {
			log.error("解密异常{}",e.getMessage(),e);
			throw e;

		}
	}


	private static byte[] encrypt(byte[] plainData, byte[] key) {
		if (plainData == null || plainData.length == 0 || key == null) {
			return new byte[0];
		}
		return toByteArray(encrypt(toIntArray(plainData, true), toIntArray(key, false)), false);
	}

	private static byte[] decrypt(byte[] cipherData, byte[] key) {
		if (cipherData == null || cipherData.length == 0 || key == null) {
			return new byte[0];
		}
		return toByteArray(decrypt(toIntArray(cipherData, false), toIntArray(key, false)), true);
	}


	private static int[] encrypt(int[] v, int[] k) {
		int n = v.length - 1;

		if (n < 1) {
			return v;
		}
		if (k.length < 4) {
			int[] key = new int[4];

			System.arraycopy(k, 0, key, 0, k.length);
			k = key;
		}
		int z = v[n];
		int y;
		int delta = 0x9E3779B9;
		int sum = 0;
		int e;
		int p;
		int q = 6 + 52 / (n + 1);

		while (q-- > 0) {
			sum = sum + delta;
			e = sum >>> 2 & 3;
			for (p = 0; p < n; p++) {
				y = v[p + 1];
				z = v[p] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y)
						+ (k[p & 3 ^ e] ^ z);
			}
			y = v[0];
			z = v[n] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y)
					+ (k[p & 3 ^ e] ^ z);
		}
		return v;
	}


	private static int[] decrypt(int[] v, int[] k) {
		int n = v.length - 1;

		if (n < 1) {
			return v;
		}
		if (k.length < 4) {
			int[] key = new int[4];

			System.arraycopy(k, 0, key, 0, k.length);
			k = key;
		}
		int z;
		int y = v[0];
		int delta = 0x9E3779B9;
		int sum;
		int  e;
		int p;
		int q = 6 + 52 / (n + 1);

		sum = q * delta;
		while (sum != 0) {
			e = sum >>> 2 & 3;
			for (p = n; p > 0; p--) {
				z = v[p - 1];
				y = v[p] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y)
						+ (k[p & 3 ^ e] ^ z);
			}
			z = v[n];
			y = v[0] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y)
					+ (k[p & 3 ^ e] ^ z);
			sum = sum - delta;
		}
		return v;
	}

	private static int[] toIntArray(byte[] data, boolean includeLength) {
		int n = (data.length & 3) == 0 ? data.length >>> 2
				: ((data.length >>> 2) + 1);
		int[] result;

		if (includeLength) {
			result = new int[n + 1];
			result[n] = data.length;
		} else {
			result = new int[n];
		}
		n = data.length;
		for (int i = 0; i < n; i++) {
			result[i >>> 2] |= (0x000000ff & data[i]) << ((i & 3) << 3);
		}
		return result;
	}


	private static byte[] toByteArray(int[] data, boolean includeLength) {
		int n = data.length << 2;
		if (includeLength) {
			int m = data[data.length - 1];

			if (m > n || m <= 0) {
				return new byte[0];
			} else {
				n = m;
			}
		}
		byte[] result = new byte[n];

		for (int i = 0; i < n; i++) {
			result[i] = (byte) ((data[i >>> 2] >>> ((i & 3) << 3)) & 0xff);
		}
		return result;
	}
}
